﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using l1cs.server.model.Instance;
using l1cs.server.datatables;
using l1cs.server.templates;
using System.Threading;
using l1cs.server.serverpackets;
using l1cs.server.utils;

namespace l1cs.server.model
{
    public class L1UltimateBattle
    {
        private int _locX;
        private int _locY;
        private L1Location _location; // 中心点
        private short _mapId;
        private int _locX1;
        private int _locY1;
        private int _locX2;
        private int _locY2;

        private int _ubId;
        private int _pattern;
        private bool _isNowUb;
        private bool _active; // UB入場可能～競技終了までtrue

        private int _minLevel;
        private int _maxLevel;
        private int _maxPlayer;

        private bool _enterRoyal;
        private bool _enterKnight;
        private bool _enterMage;
        private bool _enterElf;
        private bool _enterDarkelf;
        private bool _enterDragonKnight;
        private bool _enterIllusionist;
        private bool _enterMale;
        private bool _enterFemale;
        private bool _usePot;
        private int _hpr;
        private int _mpr;

        /// <summary>入場開始時間( 5分前から入場開始)</summary>
        private static int BEFORE_MINUTE = 5;

        private HashSet<int> _managers = new HashSet<int>();
        //private SortedSet<int> _ubTimes = new TreeSet<int>();
        private SortedSet<int> _ubTimes = new SortedSet<int>();

        private static Logger _log = Logger.getLogger( typeof( L1UltimateBattle ).FullName );

        private List<L1PcInstance> _members = new List<L1PcInstance>();

        /// <summary>
        /// ラウンド開始時のメッセージを送信する。
        /// </summary>
        /// <param name="curRound">開始するラウンド</param>
        private void sendRoundMessage( int curRound )
        {
            // XXX - このIDは間違っている
            int[] MSGID_ROUND_TABLE = { 893, 894, 895, 896 };

            sendMessage( MSGID_ROUND_TABLE[curRound - 1], "" );
        }

        /// <summary>
        /// ポーション等の補給アイテムを出現させる。
        /// </summary>
        /// <param name="curRound">現在のラウンド</param>
        private void spawnSupplies( int curRound )
        {
            if ( curRound == 1 )
            {
                spawnGroundItem( L1ItemId.ADENA, 1000, 60 );
                spawnGroundItem( L1ItemId.POTION_OF_CURE_POISON, 3, 20 );
                spawnGroundItem( L1ItemId.POTION_OF_EXTRA_HEALING, 5, 20 );
                spawnGroundItem( L1ItemId.POTION_OF_GREATER_HEALING, 3, 20 );
                spawnGroundItem( 40317, 1, 5 ); // 砥石
                spawnGroundItem( 40079, 1, 20 ); // 帰還スク
            }
            else if ( curRound == 2 )
            {
                spawnGroundItem( L1ItemId.ADENA, 5000, 50 );
                spawnGroundItem( L1ItemId.POTION_OF_CURE_POISON, 5, 20 );
                spawnGroundItem( L1ItemId.POTION_OF_EXTRA_HEALING, 10, 20 );
                spawnGroundItem( L1ItemId.POTION_OF_GREATER_HEALING, 5, 20 );
                spawnGroundItem( 40317, 1, 7 ); // 砥石
                spawnGroundItem( 40093, 1, 10 ); // ブランクスク(Lv4)
                spawnGroundItem( 40079, 1, 5 ); // 帰還スク
            }
            else if ( curRound == 3 )
            {
                spawnGroundItem( L1ItemId.ADENA, 10000, 30 );
                spawnGroundItem( L1ItemId.POTION_OF_CURE_POISON, 7, 20 );
                spawnGroundItem( L1ItemId.POTION_OF_EXTRA_HEALING, 20, 20 );
                spawnGroundItem( L1ItemId.POTION_OF_GREATER_HEALING, 10, 20 );
                spawnGroundItem( 40317, 1, 10 ); // 砥石
                spawnGroundItem( 40094, 1, 10 ); // ブランクスク(Lv5)
            }
        }

        /// <summary>
        /// コロシアムから出たメンバーをメンバーリストから削除する。
        /// </summary>
        private void removeRetiredMembers()
        {
            L1PcInstance[] temp = getMembersArray();
            for ( int i = 0; i < temp.Length; i++ )
            {
                if ( temp[i].getMapId() != _mapId )
                {
                    removeMember( temp[i] );
                }
            }
        }

        /// <summary>
        /// UBに参加しているプレイヤーへメッセージ(S_ServerMessage)を送信する。
        /// </summary>
        /// <param name="type">メッセージタイプ</param>
        /// <param name="msg">送信するメッセージ</param>
        private void sendMessage( int type, String msg )
        {
            foreach ( L1PcInstance pc in getMembersArray() )
            {
                pc.sendPackets( new S_ServerMessage( type, msg ) );
            }
        }

        /// <summary>
        /// コロシアム上へアイテムを出現させる。
        /// </summary>
        /// <param name="itemId">出現させるアイテムのアイテムID</param>
        /// <param name="stackCount">アイテムのスタック数</param>
        /// <param name="count">出現させる数</param>
        private void spawnGroundItem( int itemId, int stackCount, int count )
        {
            L1Item temp = ItemTable.getInstance().getTemplate( itemId );
            if ( temp == null )
            {
                return;
            }

            for ( int i = 0; i < count; i++ )
            {
                L1Location loc = _location.randomLocation( ( getLocX2() - getLocX1() ) / 2, false );
                if ( temp.isStackable() )
                {
                    L1ItemInstance item = ItemTable.getInstance().createItem( itemId );
                    item.setEnchantLevel( 0 );
                    item.setCount( stackCount );
                    L1GroundInventory ground = L1World.getInstance().getInventory( loc.getX(), loc.getY(), _mapId );
                    if ( ground.checkAddItem( item, stackCount ) == L1Inventory.OK )
                    {
                        ground.storeItem( item );
                    }
                }
                else
                {
                    L1ItemInstance item = null;
                    for ( int createCount = 0; createCount < stackCount; createCount++ )
                    {
                        item = ItemTable.getInstance().createItem( itemId );
                        item.setEnchantLevel( 0 );
                        L1GroundInventory ground = L1World.getInstance().getInventory( loc.getX(), loc.getY(), _mapId );
                        if ( ground.checkAddItem( item, stackCount ) == L1Inventory.OK )
                        {
                            ground.storeItem( item );
                        }
                    }
                }
            }
        }

        /// <summary>
        /// コロシアム上のアイテムとモンスターを全て削除する。
        /// </summary>
        private void clearColosseum()
        {
            foreach ( Object obj in L1World.getInstance().getVisibleObjects( _mapId ).Values )
            {
                if ( obj is L1MonsterInstance ) // モンスター削除
                {
                    L1MonsterInstance mob = (L1MonsterInstance)obj;
                    if ( !mob.isDead() )
                    {
                        mob.setDead( true );
                        mob.setStatus( ActionCodes.ACTION_Die );
                        mob.setCurrentHpDirect( 0 );
                        mob.deleteMe();

                    }
                }
                else if ( obj is L1Inventory ) // アイテム削除
                {
                    L1Inventory inventory = (L1Inventory)obj;
                    inventory.clearItems();
                }
            }
        }

        /// <summary>
        /// コンストラクタ。
        /// </summary>
        public L1UltimateBattle()
        {
        }

        //class UbThread
        //{//implements Runnable {

        /// <summary>
        /// 競技開始までをカウントダウンする。
        /// </summary>
        private void countDown()
        {
            // XXX - このIDは間違っている
            const int MSGID_COUNT = 637;
            const int MSGID_START = 632;

            for ( int loop = 0; loop < BEFORE_MINUTE * 60 - 10; loop++ )
            { // 開始10秒前まで待つ
                Thread.Sleep( 1000 );
                // removeRetiredMembers();
            }
            removeRetiredMembers();

            sendMessage( MSGID_COUNT, "10" ); // 10秒前

            Thread.Sleep( 5000 );
            sendMessage( MSGID_COUNT, "5" ); // 5秒前

            Thread.Sleep( 1000 );
            sendMessage( MSGID_COUNT, "4" ); // 4秒前

            Thread.Sleep( 1000 );
            sendMessage( MSGID_COUNT, "3" ); // 3秒前

            Thread.Sleep( 1000 );
            sendMessage( MSGID_COUNT, "2" ); // 2秒前

            Thread.Sleep( 1000 );
            sendMessage( MSGID_COUNT, "1" ); // 1秒前

            Thread.Sleep( 1000 );
            sendMessage( MSGID_START, "アルティメット バトル" ); // スタート
            removeRetiredMembers();
        }

        /// <summary>
        /// 全てのモンスターが出現した後、次のラウンドが始まるまでの時間を待機する。
        /// </summary>
        /// <param name="curRound">現在のラウンド</param>
        private void waitForNextRound( int curRound )
        {
            int[] WAIT_TIME_TABLE = { 6, 6, 2, 18 };

            int wait = WAIT_TIME_TABLE[curRound - 1];
            for ( int i = 0; i < wait; i++ )
            {
                Thread.Sleep( 10000 );
                // removeRetiredMembers();
            }
            removeRetiredMembers();
        }

        /// <summary>
        /// スレッドプロシージャ。
        /// </summary>
        /// <param name="state"></param>
        public void run( Object state )
        {

            setActive( true );
            countDown();
            setNowUb( true );
            for ( int round = 1; round <= 4; round++ )
            {
                sendRoundMessage( round );

                L1UbPattern pattern = UBSpawnTable.getInstance().getPattern( _ubId, _pattern );

                List<L1UbSpawn> spawnList = pattern.getSpawnList( round );

                foreach ( L1UbSpawn spawn in spawnList )
                {
                    if ( getMembersCount() > 0 )
                    {
                        spawn.spawnAll();
                    }

                    Thread.Sleep( spawn.getSpawnDelay() * 1000 );
                    // removeRetiredMembers();
                }

                if ( getMembersCount() > 0 )
                {
                    spawnSupplies( round );
                }

                waitForNextRound( round );
            }

            foreach ( L1PcInstance pc in getMembersArray() ) // コロシアム内に居るPCを外へ出す
            {
                Random random = new Random();
                int rndx = random.Next( 4 );
                int rndy = random.Next( 4 );
                int locx = 33503 + rndx;
                int locy = 32764 + rndy;
                short mapid = 4;
                L1Teleport.teleport( pc, locx, locy, mapid, 5, true );
                removeMember( pc );
            }
            clearColosseum();
            setActive( false );
            setNowUb( false );
        }
        //}

        /// <summary>
        /// アルティメットバトルを開始する。
        /// </summary>
        public void start()
        {
            int patternsMax = UBSpawnTable.getInstance().getMaxPattern( _ubId );
            Random random = new Random();
            _pattern = random.Next( patternsMax ) + 1; // 出現パターンを決める

            //UbThread ub = new UbThread();
            //GeneralThreadPool.getInstance().execute(ub);
            ThreadPool.QueueUserWorkItem( new WaitCallback( run ) );
        }

        /// <summary>
        /// プレイヤーを参加メンバーリストへ追加する。
        /// </summary>
        /// <param name="pc">新たに参加するプレイヤー</param>
        public void addMember( L1PcInstance pc )
        {
            if ( !_members.Contains( pc ) )
            {
                _members.Add( pc );
            }
        }

        /// <summary>
        /// プレイヤーを参加メンバーリストから削除する。
        /// </summary>
        /// <param name="pc">pc削除するプレイヤー</param>
        public void removeMember( L1PcInstance pc )
        {
            _members.Remove( pc );
        }

        /// <summary>
        /// 参加メンバーリストをクリアする。
        /// </summary>
        public void clearMembers()
        {
            _members.Clear();
        }

        /// <summary>
        /// プレイヤーが、参加メンバーかを返す。
        /// </summary>
        /// <param name="pc">調べるプレイヤー</param>
        /// <returns>参加メンバーであればtrue、そうでなければfalse</returns>
        public bool isMember( L1PcInstance pc )
        {
            return _members.Contains( pc );
        }

        /// <summary>
        /// 参加メンバーの配列を作成し、返す。
        /// </summary>
        /// <returns>参加メンバーの配列</returns>
        public L1PcInstance[] getMembersArray()
        {
            return _members.ToArray();
        }

        /// <summary>
        /// 参加メンバー数を返す。
        /// </summary>
        /// <returns>参加メンバー数</returns>
        public int getMembersCount()
        {
            return _members.Count;
        }

        /// <summary>
        /// UB中かを設定する。
        /// </summary>
        /// <param name="i">true/false</param>
        private void setNowUb( bool i )
        {
            _isNowUb = i;
        }

        /// <summary>
        /// UB中かを返す。
        /// </summary>
        /// <returns>UB中であればtrue、そうでなければfalse。</returns>
        public bool isNowUb()
        {
            return _isNowUb;
        }

        public int getUbId()
        {
            return _ubId;
        }

        public void setUbId( int id )
        {
            _ubId = id;
        }

        public short getMapId()
        {
            return _mapId;
        }

        public void setMapId( short mapId )
        {
            this._mapId = mapId;
        }

        public int getMinLevel()
        {
            return _minLevel;
        }

        public void setMinLevel( int level )
        {
            _minLevel = level;
        }

        public int getMaxLevel()
        {
            return _maxLevel;
        }

        public void setMaxLevel( int level )
        {
            _maxLevel = level;
        }

        public int getMaxPlayer()
        {
            return _maxPlayer;
        }

        public void setMaxPlayer( int count )
        {
            _maxPlayer = count;
        }

        public void setEnterRoyal( bool enterRoyal )
        {
            this._enterRoyal = enterRoyal;
        }

        public void setEnterKnight( bool enterKnight )
        {
            this._enterKnight = enterKnight;
        }

        public void setEnterMage( bool enterMage )
        {
            this._enterMage = enterMage;
        }

        public void setEnterElf( bool enterElf )
        {
            this._enterElf = enterElf;
        }

        public void setEnterDarkelf( bool enterDarkelf )
        {
            this._enterDarkelf = enterDarkelf;
        }

        public void setEnterDragonKnight( bool enterDragonKnight )
        {
            this._enterDragonKnight = enterDragonKnight;
        }

        public void setEnterIllusionist( bool enterIllusionist )
        {
            this._enterIllusionist = enterIllusionist;
        }

        public void setEnterMale( bool enterMale )
        {
            this._enterMale = enterMale;
        }

        public void setEnterFemale( bool enterFemale )
        {
            this._enterFemale = enterFemale;
        }

        public bool canUsePot()
        {
            return _usePot;
        }

        public void setUsePot( bool usePot )
        {
            this._usePot = usePot;
        }

        public int getHpr()
        {
            return _hpr;
        }

        public void setHpr( int hpr )
        {
            this._hpr = hpr;
        }

        public int getMpr()
        {
            return _mpr;
        }

        public void setMpr( int mpr )
        {
            this._mpr = mpr;
        }

        public int getLocX1()
        {
            return _locX1;
        }

        public void setLocX1( int locX1 )
        {
            this._locX1 = locX1;
        }

        public int getLocY1()
        {
            return _locY1;
        }

        public void setLocY1( int locY1 )
        {
            this._locY1 = locY1;
        }

        public int getLocX2()
        {
            return _locX2;
        }

        public void setLocX2( int locX2 )
        {
            this._locX2 = locX2;
        }

        public int getLocY2()
        {
            return _locY2;
        }

        public void setLocY2( int locY2 )
        {
            this._locY2 = locY2;
        }

        /// <summary>
        /// setされたlocx1～locy2から中心点を求める。
        /// </summary>
        public void resetLoc()
        {
            _locX = ( _locX2 + _locX1 ) / 2;
            _locY = ( _locY2 + _locY1 ) / 2;
            _location = new L1Location( _locX, _locY, _mapId );
        }

        public L1Location getLocation()
        {
            return _location;
        }

        public void addManager( int npcId )
        {
            _managers.Add( npcId );
        }

        public bool containsManager( int npcId )
        {
            return _managers.Contains( npcId );
        }

        public void addUbTime( int time )
        {
            _ubTimes.Add( time );
        }

        public String getNextUbTime()
        {
            return intToTimeFormat( nextUbTime() );
        }

        private int nextUbTime()
        {
            return 1;
            //SimpleDateFormat sdf = new SimpleDateFormat( "HHmm" );
            //int nowTime = Integer.valueOf( sdf.format( getRealTime().getTime() ) );
            int nowTime = Convert.ToInt32( DateTime.Now.ToString( "HHmm" ) );
            //SortedSet<int> tailSet = _ubTimes.TailSet( nowTime );
            //if ( tailSet.isEmpty() )
            //{
            //    tailSet = _ubTimes;
            //}
            //return tailSet.first();
            // TODO 優先度2 SortedSet#tailSetの実装がいまいち
            if ( _ubTimes.Last() <= nowTime )
            {
                return _ubTimes.First();
            }
            else
            {
                return _ubTimes.GetViewBetween( 2, _ubTimes.Max ).First();
            }
        }

        private static String intToTimeFormat( int n )
        {
            return n / 100 + ":" + n % 100 / 10 + "" + n % 10;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public bool checkUbTime()
        {
            DateTime realTime = DateTime.Now;
            realTime.AddMinutes( BEFORE_MINUTE );
            int nowTime = SqlUtils.toInt( realTime.ToString( "HHmm" ) );
            return _ubTimes.Contains( nowTime );
        }

        private void setActive( bool f )
        {
            _active = f;
        }

        /// <summary>
        /// UB入場可能～競技終了まではtrue,それ以外はfalseを返す。
        /// </summary>
        /// <returns></returns>
        public bool isActive()
        {
            return _active;
        }

        /// <summary>
        /// UBに参加可能か、レベル、クラスをチェックする。
        /// </summary>
        /// <param name="pc">UBに参加できるかチェックするPC</param>
        /// <returns>参加出来る場合はtrue,出来ない場合はfalse</returns>
        public bool canPcEnter( L1PcInstance pc )
        {
            _log.log( Level.FINE, "pcname=" + pc.getName() + " ubid=" + _ubId
                    + " minlvl=" + _minLevel + " maxlvl=" + _maxLevel );
            // 参加可能なレベルか
            if ( !IntRange.includes( pc.getLevel(), _minLevel, _maxLevel ) )
            {
                return false;
            }

            // 参加可能なクラスか
            if ( !( ( pc.isCrown() && _enterRoyal ) || ( pc.isKnight() && _enterKnight )
                    || ( pc.isWizard() && _enterMage ) || ( pc.isElf() && _enterElf )
                    || ( pc.isDarkelf() && _enterDarkelf )
                    || ( pc.isDragonKnight() && _enterDragonKnight )
                    || ( pc.isIllusionist() && _enterIllusionist ) ) )
            {
                return false;
            }

            return true;
        }

        private String[] _ubInfo;

        public String[] makeUbInfoStrings()
        {
            if ( _ubInfo != null )
            {
                return _ubInfo;
            }
            String nextUbTime = getNextUbTime();
            // クラス
            StringBuilder classesBuff = new StringBuilder();
            if ( _enterDarkelf )
            {
                classesBuff.Append( "ダーク エルフ " );
            }
            if ( _enterMage )
            {
                classesBuff.Append( "ウィザード " );
            }
            if ( _enterElf )
            {
                classesBuff.Append( "エルフ " );
            }
            if ( _enterKnight )
            {
                classesBuff.Append( "ナイト " );
            }
            if ( _enterRoyal )
            {
                classesBuff.Append( "プリンス " );
            }
            if ( _enterDragonKnight )
            {
                classesBuff.Append( "ドラゴンナイト " );
            }
            if ( _enterIllusionist )
            {
                classesBuff.Append( "イリュージョニスト " );
            }
            String classes = classesBuff.ToString().Trim();
            // 性別
            StringBuilder sexBuff = new StringBuilder();
            if ( _enterMale )
            {
                sexBuff.Append( "男 " );
            }
            if ( _enterFemale )
            {
                sexBuff.Append( "女 " );
            }
            String sex = sexBuff.ToString().Trim();
            String loLevel = Convert.ToString( _minLevel );
            String hiLevel = Convert.ToString( _maxLevel );
            String teleport = _location.getMap().isEscapable() ? "可能" : "不可能";
            String res = _location.getMap().isUseResurrection() ? "可能" : "不可能";
            String pot = "可能";
            String hpr = Convert.ToString( _hpr );
            String mpr = Convert.ToString( _mpr );
            String summon = _location.getMap().isTakePets() ? "可能" : "不可能";
            String summon2 = _location.getMap().isRecallPets() ? "可能" : "不可能";
            _ubInfo = new String[] { nextUbTime, classes, sex, loLevel, hiLevel,
                teleport, res, pot, hpr, mpr, summon, summon2 };
            return _ubInfo;
        }
    }
}
